package com.ling.module1sbbyyds.service.news.chat;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.github.yitter.idgen.YitIdHelper;
import com.ling.sbbyyds.entity.news.ChatReceiveParams;
import com.ling.sbbyyds.entity.news.ChatUserInfo;
import com.ling.sbbyyds.entity.news.UserChatRoom;
import com.ling.sbbyyds.entity.news.table.NewsChatMessage;
import com.ling.sbbyyds.entity.news.table.NewsChatRoom;
import com.ling.sbbyyds.entity.news.table.NewsUserChat;
import com.ling.sbbyyds.entity.users.User;
import com.ling.module1sbbyyds.utils.FileLocalStorageTool;
import com.ling.sbbyyds.attribute.FixedAttribute;
import com.ling.sbbyyds.datatools.DataConversionUtils;
import com.ling.sbbyyds.imagetools.ImageUtils;
import com.ling.sbbyyds.interaction.ResultVo;
import com.ling.sbbyyds.mapper.news.ChatRoomMapper;
import com.ling.sbbyyds.mapper.news.NewsChatMessageMapper;
import com.ling.sbbyyds.mapper.news.NewsChatRoomMapper;
import com.ling.sbbyyds.mapper.news.NewsUserChatMapper;
import com.unfbx.chatgpt.OpenAiClient;
import com.unfbx.chatgpt.entity.completions.Completion;
import com.unfbx.chatgpt.entity.completions.CompletionResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import javax.websocket.Session;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Description: 聊天室业务
 * @author: ling
 * @date: 2023年02月12日
 */
@Slf4j
@Service
public class ChatRoomService {

    @Autowired
    private ChatRoomMapper chatRoomMapper;
    @Autowired
    private RedisTemplate<String, String> redis;
    @Autowired
    private NewsUserChatMapper newsUserChatMapper;
    @Autowired
    private NewsChatRoomMapper newsChatRoomMapper;
    @Autowired
    private NewsChatMessageMapper newsChatMessageMapper;
    @Autowired
    private FileLocalStorageTool fileLocalStorageTool;
    private final String chatImageFolder = "chatRoomImage";
    Random random = new Random();
    /**
     * openAI apikey
     */
    String apiKey = "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";
    OpenAiClient openAiClient = new OpenAiClient(apiKey);
    public static final ConcurrentHashMap<Long, HashMap<String, Session>> webSocketMapUserList =
            new ConcurrentHashMap<>();

    /**
     * 根据用户ID获取用户显式的聊天室
     *
     * @param userId  用户ID
     * @param session 客户会话
     */
    public void getUserChatRoomInfoList(Long userId, Session session) {
        // 获取用户显示的聊天室信息(信息显示最新的一页)
        List<UserChatRoom> chatRoomInfoList = chatRoomMapper.getShowChatRoomInfoList(userId);

        for (UserChatRoom userChatRoom : chatRoomInfoList) {
            Long chatRoomId = userChatRoom.getChatRoomId();
            // 聊天室ID获取初始化50条聊天信息
            List<NewsChatMessage> chatMessageList = chatRoomMapper.getChatRoomMessageList(chatRoomId);
            Collections.reverse(chatMessageList);
            // 查询聊天室内所有用户
            List<ChatUserInfo> chatUserInfoList = chatRoomMapper.queryChatRoomUserList(chatRoomId);
            // 显示私聊房间号好友ID
            if (FixedAttribute.CHAT_ROOM_TYPE_PRIVATE.equals(userChatRoom.getChatRoomType())){
                chatUserInfoList.forEach(item -> {if (!userId.equals(item.getUserId())){
                    userChatRoom.setFriendId(item.getUserId());
                }});
            }
            chatUserInfoList.forEach(item->item.setAvatar(ImageUtils.convertImageUrl(item.getAvatar())));
            userChatRoom.setChatContents(chatMessageList)
                    .setChatUserList(chatUserInfoList);
            // 推送单个聊天室信息到连接客户端
            this.pushDataToClient(userChatRoom, Arrays.asList(session), FixedAttribute.CHAT_MESSAGE_TYPE_ROOM);
        }
    }

    /**
     * 用户会话连接
     *
     * @param token   连接用户token
     * @param session 操作会话
     */
    public void clientOpenConnection(String token, Session session) {
        //判断用户是否登录
        User user = checkToken(token);
        //1.1.判断用户是否登录（校验token）
        if (ObjectUtils.isEmpty(user)) {
            log.error("用户未登录！");
            return;
        }
        Long userId = user.getId();
        // 将用户ID存进会话中
        session.getUserProperties().put("userId", userId);

        this.saveSession(userId, token, session);

        // 建立连接，用户显示聊天室列表以及聊天室内容并推送给用户
        this.getUserChatRoomInfoList(userId, session);
    }

    /**
     * 用户会话关闭
     *
     * @param session 操作会话
     */
    public void clientSessionClose(Session session) {
        Long userId = (Long) session.getUserProperties().get("userId");
        HashMap<String, Session> userSessionMap = webSocketMapUserList.get(userId);
        for (String key : userSessionMap.keySet()) {
            if (session.equals(userSessionMap.get(key))) {
                // 将会话从用户列表中清除
                userSessionMap.remove(key);
                // 如果用户会话列表为空则彻底清除用户的会话存储
                if (userSessionMap.isEmpty()) {
                    webSocketMapUserList.remove(userId);
                }
                break;
            }
        }
    }

    /**
     * 客户信息交互
     *
     * @param message 客户发送过来的信息
     */
    public void clientChatOperation(String message) {
        ChatReceiveParams params = (ChatReceiveParams) DataConversionUtils.objectJsonToObject(message, ChatReceiveParams.class);
        Long userId = params.getUserId();
        // 交互操作
        switch (params.getOperationType()) {
            // 私聊信息发送
            case "SPR":
                this.sendToPrivateChat(params);
                break;
            // 群聊创建
            case "CGR":
                this.createGroupChat(params);
                break;
            //// 群聊信息发送
            case "SGR":
                this.sendToGroupChat(params);
                break;
            // 用户显示聊天室清除
            //case "CLG":
            //    this.userCloseChatRoom(params);
            //    break;
            default:
                //this.errorPush("参数operationType的值有误,找不到此操作", webSocketMapUserList.get(userId));
                break;
        }
    }

    /**
     * 客户发送群聊信息
     *
     * @param params
     */
    private void sendToGroupChat(ChatReceiveParams params) {

    }

    /**
     * 创建群聊房间
     *
     * @param params 参数
     */
    private void createGroupChat(ChatReceiveParams params) {
        Long[] groupUserId = params.getGroupUserId();
        String groupChatHead = params.getGroupChatHead();
        Long userId = params.getUserId();
        if (ObjectUtils.isEmpty(groupUserId) || groupUserId.length < 2) {
            //this.errorPush("群成员数不满足创建条件", webSocketMapUserList.get(userId));
        }
        if (!ObjectUtils.isEmpty(groupChatHead)) {
            try {
                groupChatHead = fileLocalStorageTool.uploadBase64Image(chatImageFolder, groupChatHead);
            } catch (IOException e) {
                //this.errorPush("头像解析失败", webSocketMapUserList.get(userId));
            }
        }

        NewsChatRoom newsChatRoom = new NewsChatRoom()
                .setChatRoomName("爆爆新群聊")
                .setChatRoomType(FixedAttribute.CHAT_ROOM_TYPE_PUBLIC)
                .setChatImagePath(groupChatHead)
                .setChatRootUserId(userId);
        newsChatRoomMapper.insert(newsChatRoom);
        Long chatRoomId = newsChatRoom.getId();
        // 将创建人添加到群成员中做关联
        Long[] newArray = Arrays.copyOf(groupUserId, groupUserId.length + 1);
        newArray[newArray.length - 1] = userId;
        this.createRelation(chatRoomId, Arrays.asList(newArray));

        // 查询聊天室内所有用户
        List<ChatUserInfo> chatUserInfoList = chatRoomMapper.queryChatRoomUserList(chatRoomId);
        UserChatRoom userChatRoom = new UserChatRoom();
        userChatRoom.setChatContents(null)
                .setChatUserList(chatUserInfoList)
                .setChatRoomType(FixedAttribute.CHAT_ROOM_TYPE_PUBLIC)
                .setChatRoomName("爆爆新群聊")
                .setChatRoomId(chatRoomId);
//        this.pushDataToClient(userChatRoom, newArray, FixedAttribute.CHAT_PUSH_TYPE_ROOM);
    }

    /**
     * 发送信息到私聊聊天室
     *
     * @param params 请求参数
     */
    private void sendToPrivateChat(ChatReceiveParams params) {
        // 如果聊天室信息记录存在则直接找到聊天记录进行追加，否则
        Long chatRoomId = params.getChatRoomId();
        Long userId = params.getUserId();
        Long receiveUserId = params.getReceiveUserId();
        String sendContent = params.getSendContent();
        String messageType = params.getMessageType();

        // 调用GPT
        if (sendContent.contains("@鼠鼠") || sendContent.contains("@杰杰") || sendContent.contains("@手手")) {
            // TODO 创建虚拟用户 推送信息给发送端客户
            NewsChatMessage newsChatMessage = new NewsChatMessage()
                    .setChatRoomId(chatRoomId)
                    .setMessageType(FixedAttribute.CHAT_CONTENT_TYPE_TEXT)
                    .setCreateDate(new Date())
                    .setContent(this.getGPTMessage(sendContent))
                    .setSendUserId(0L);
            this.pushDataToClient(newsChatMessage, new Long[]{userId}, FixedAttribute.CHAT_MESSAGE_TYPE_MESSAGE);
            return;
        }
        if (ObjectUtils.isEmpty(chatRoomId)) {
            // 用户端聊天室不存在，创建聊天室
            NewsChatRoom newsChatRoom = new NewsChatRoom()
                    .setId(YitIdHelper.nextId())
                    .setChatRoomType(FixedAttribute.CHAT_ROOM_TYPE_PRIVATE);
            newsChatRoomMapper.insert(newsChatRoom);
            chatRoomId = newsChatRoom.getId();
            // 创建用户与聊天室的关联数据
            this.createRelation(chatRoomId, Arrays.asList(userId, receiveUserId));
            // 查询聊天室内所有用户
            List<ChatUserInfo> chatUserInfoList = chatRoomMapper.queryChatRoomUserList(chatRoomId);

            NewsChatMessage newsChatMessage = new NewsChatMessage()
                    .setId(YitIdHelper.nextId())
                    .setChatRoomId(chatRoomId)
                    .setMessageType(messageType)
                    .setContent(sendContent)
                    .setSendUserId(params.getUserId());
            newsChatMessageMapper.insert(newsChatMessage);
            // 向接收者推送信息(如果在线) 推新聊天室
            UserChatRoom userChatRoom = new UserChatRoom()
                    .setChatRoomId(chatRoomId)
                    .setChatContents(Arrays.asList(newsChatMessage))
                    .setChatRoomType(FixedAttribute.CHAT_ROOM_TYPE_PRIVATE)
                    .setChatUserList(chatUserInfoList)
                    .setNotReadNum(1);
            this.pushDataToClient(userChatRoom, new Long[]{receiveUserId}, FixedAttribute.CHAT_MESSAGE_TYPE_ROOM);
        } else {
            // TODO roomID + message + userID + 插入聊天信息
            NewsChatMessage chatMessage = new NewsChatMessage();
            chatMessage.setId(YitIdHelper.nextId())
                    .setChatRoomId(chatRoomId)
                    .setSendUserId(userId)
                    .setContent(sendContent)
                    .setMessageType(messageType);
            newsChatMessageMapper.insert(chatMessage);
            List<Long> collect = new ArrayList<>();
            collect.add(receiveUserId);

            // 如果聊天室用户在线 推送单条信息
            this.pushDataToClient(chatMessage, collect.toArray(new Long[0]), FixedAttribute.CHAT_MESSAGE_TYPE_MESSAGE);
        }
    }

    /**
     * 批量创建聊天室与用户关联
     *
     * @param roomId 聊天室ID
     * @param asList 用户ID列表
     */
    private void createRelation(Long roomId, List<Long> asList) {
        ArrayList<NewsUserChat> newsUserChats = new ArrayList<>(asList.size());
        asList.forEach(item -> {
            NewsUserChat newsUserChat = new NewsUserChat()
                    .setId(YitIdHelper.nextId())
                    .setChatRoomId(roomId)
                    .setUserId(item)
                    .setIsDelete(0);
            newsUserChats.add(newsUserChat);
        });
        newsUserChatMapper.insertList(newsUserChats);
    }

    /**
     * 服务器主动推送消息 给客户端
     *
     * @param message 信息
     * @param userIds 用户列表
     */
    private void sendMessage(String message, Long[] userIds) {
        ArrayList<Session> sessions = new ArrayList<>();
        for (Long userId : userIds) {
            HashMap<String, Session> userSessions = webSocketMapUserList.get(userId);
            if (!ObjectUtils.isEmpty(userSessions)) {
                for (String key : userSessions.keySet()) {
                    sessions.add(userSessions.get(key));
                }
            }
        }
        this.sendMessage(message, sessions);
    }

    /**
     * 服务器主动推送消息 给客户端
     *
     * @param message     信息
     * @param sessionList 会话列表
     */
    private void sendMessage(String message, List<Session> sessionList) {
        sessionList.forEach(item -> {
            try {
                item.getBasicRemote().sendText(message);
            } catch (Exception e) {
                log.error("消息发送失败！", e);
            }
        });
    }

    /**
     * 保存用户会话
     *
     * @param userId  用户ID
     * @param token 当前连接token标识
     * @param session 当前会话
     */
    private void saveSession(Long userId, String token, Session session) {
        // 如果用户有会话存在则添加进会话列表，否则初始化会话列表
        HashMap<String, Session> sessionMap = webSocketMapUserList.get(userId);
        if (ObjectUtils.isEmpty(sessionMap)) {
            sessionMap = new HashMap<>();
        }
        sessionMap.put(token,session);
        webSocketMapUserList.put(userId, sessionMap);
    }

    /**
     * 校验token
     *
     * @param token 用户token
     * @return UserInfo 用户信息
     */
    public User checkToken(String token) {
        User user = null;
        if (token != null) {
            //获取redis中是否有这个token   key:token  value: User(用户信息)
            String jsonUser = redis.opsForValue().get(token);
            if (StringUtils.isNotBlank(jsonUser)) {
                user = JSON.parseObject(jsonUser, User.class);
            }
            return user;
        }
        return null;
    }

    /**
     * 推送失败反馈回前端
     *
     * @param message 信息
     * @param session 用户会话
     */
    private void errorPush(String message, List<Session> session) {
        String messageStr = JSON.toJSONString(ResultVo.fail(message));
        this.sendMessage(messageStr, session);
    }

    /**
     * 根据用户ID
     * 推送成功反馈回前端
     *
     * @param userIds       用户ID
     * @param data          数据
     * @param serverMesType 信息类型
     */
    private void pushDataToClient(Object data, Long[] userIds, String serverMesType) {
        JSONObject jsonObject = (JSONObject) JSON.toJSON(data);
        jsonObject.put("serverMesType", serverMesType);
        String messageStr = JSON.toJSONString(ResultVo.success(jsonObject));
        this.sendMessage(messageStr, userIds);
    }

    /**
     * 根据用户会话
     * 推送成功反馈回前端
     *
     * @param data          数据
     * @param sessions      用户会话
     * @param serverMesType 信息类型
     */
    private void pushDataToClient(Object data, List<Session> sessions, String serverMesType) {
        JSONObject jsonObject = (JSONObject) JSON.toJSON(data);
        jsonObject.put("serverMesType", serverMesType);
        String messageStr = JSON.toJSONString(ResultVo.success(jsonObject));
        this.sendMessage(messageStr, sessions);
    }

    /**
     * 请求GPT模型获取信息
     *
     * @param sendMessage 客户发送信息
     * @return GPT回复信息
     */
    private String getGPTMessage(String sendMessage) {

        int maxToken = random.nextInt(90) + 10;
        float temperature = (random.nextInt(4) + 1) / 10;
        Completion completion = Completion.builder()
                .maxTokens(maxToken)
                .temperature(temperature)
                .prompt(sendMessage)
                .build();

        CompletionResponse completions = openAiClient.completions(completion);
        StringBuilder stringBuilder = new StringBuilder();
        Arrays.stream(completions.getChoices()).forEach(item -> {
            stringBuilder.append(item.getText());
        });
        return stringBuilder.toString().trim();
    }


}
